﻿using System;
using System.Linq;
using NDBGen.Descriptors;
using NDBGen.Descriptors.Columns.DataTypes;
using NDBGen.Descriptors.Columns.Tokens;
using NDBGen.Descriptors.Tables.Tokens;

namespace NDBGen.Plugins.Database
{
    public class DatabaseGeneratorPluginData : PluginData<IColumnDataTypeScriptSnippetGenerator, IColumnTokenScriptSnippetGenerator, ITableTokenScriptSnippetGenerator>
    {
        public DatabaseGeneratorPluginData(ParsedInput parsedInput) : base(parsedInput) { }
    }

    public static class DatabasePluginDataExtensions
    {
        private static void CheckArgs(IGenerator generator, IDescriptor descriptor)
        {
            if (generator == null) throw new ArgumentNullException("generator");
            if (generator.PluginData == null) throw new ArgumentNullException("generator.PluginData");
            if (descriptor == null) throw new ArgumentNullException("descriptor");
        }

        public static IColumnDataTypeScriptSnippetGenerator GetGeneratorForDescriptor(this IGenerator generator, IColumnDataTypeDescriptor descriptor)
        {
            CheckArgs(generator, descriptor);
            if (!generator.PluginData.ColumnDataTypeGenerators.Any()) throw new Exception("Could not find any ColumnDataTypeGenerator implementations.");

            try
            {
                Type generatorType;
                if (generator.PluginData.ColumnDataTypeGenerators.TryGetValue(descriptor.GetType(), out generatorType))
                {
                    return ActivateGenerator<IColumnDataTypeScriptSnippetGenerator, IColumnDataTypeDescriptor>(generatorType, generator.PluginData, descriptor);
                }
                else return null;
            }
            catch (Exception ex)
            {
                throw new Exception(String.Format("Could not retrieve generator for descriptor '{0}'", descriptor), ex);
            }
        }

        public static IColumnTokenScriptSnippetGenerator GetGeneratorForDescriptor(this IGenerator generator, IColumnTokenDescriptor descriptor)
        {
            CheckArgs(generator, descriptor);
            if (!generator.PluginData.ColumnTokenGenerators.Any()) throw new Exception("Could not find any ColumnTokenGenerator implementations.");

            try
            {
                Type generatorType;
                if (generator.PluginData.ColumnTokenGenerators.TryGetValue(descriptor.GetType(), out generatorType))
                {
                    return ActivateGenerator<IColumnTokenScriptSnippetGenerator, IColumnTokenDescriptor>(generatorType, generator.PluginData, descriptor);
                }
                else return null;
            }
            catch (Exception ex)
            {
                throw new Exception(String.Format("Could not retrieve generator for descriptor '{0}'", descriptor), ex);
            }
        }

        public static ITableTokenScriptSnippetGenerator GetGeneratorForDescriptor(this IGenerator generator, ITableTokenDescriptor descriptor)
        {
            CheckArgs(generator, descriptor);
            if (!generator.PluginData.TableTokenGenerators.Any()) throw new Exception("Could not find any TableTokenGenerator implementations.");

            try
            {
                Type generatorType;
                if (generator.PluginData.TableTokenGenerators.TryGetValue(descriptor.GetType(), out generatorType))
                {
                    return ActivateGenerator<ITableTokenScriptSnippetGenerator, ITableTokenDescriptor>(generatorType, generator.PluginData, descriptor);
                }
                else return null;
            }
            catch (Exception ex)
            {
                throw new Exception(String.Format("Could not retrieve generator for descriptor '{0}'", descriptor), ex);
            }
        }

        private static TGenerator ActivateGenerator<TGenerator, TDescriptor>(Type generatorType, IPluginData pluginData, TDescriptor descriptor)
            where TGenerator : IGenerator<TDescriptor>
            where TDescriptor : IDescriptor
        {
            var generator = (TGenerator)Activator.CreateInstance(generatorType);
            generator.Init(pluginData, descriptor);

            return generator;
        }
    }
}